// 简易状态机相关操作
package xstate

import (
	"errors"
	"strings"
	"sync"
)

// 状态机是一种行为模型。它由有限数量的状态组成，因此也称为有限状态机（FSM，finite state machine）
// 示例：
//  某灯泡初始化后状态为关闭状态
//  关闭状态下，允许有开启的操作，该操作将状态切换为开启
//  开启状态下，允许有关闭的操作，该操作将状态切换为关闭
//  开启状态下，允许有重置的操作，该操作将状态切换为初始关闭状态
//
// 以上示例使用直观符号标识如下：
//	close:关闭,open:开启 // 允许的状态列表，第一个为初始状态
//	open:close->open
//	close:open->close
//	reset:open,close->close

// 状态机定义
// 此状态机模仿了 github.com/looplab/fsm 的实现方案
type Xstate struct {
	start        string                                       // 开始状态
	current      string                                       // 当前状态
	events       []event                                      // 允许的操作列表
	Error        error                                        // 过程中出现的错误
	statusMap    map[string]string                            // 允许的状态列表
	data         []string                                     // 状态机内部携带的参数信息
	hookSwitch   []func(current, target string, dat []string) // 切换开始事件钩子(当前状态,目标状态,附加参数)
	hookEvent    map[string][]func(e string, dat []string)    // 事件运行开始回调(当前状态,附加参数)
	sync.RWMutex                                              // 并发锁
}

// 根据字符串来创建状态机
// 所有的事件必须通过统一入口进行添加，不允许中途进行添加/修改
// 监听事件为后续进行添加的，不做强制要求
// PS:
// - 回调部分使用go携程进行回调，所以可能会出现先后顺序不统一问题
//
//	str	待格式化的字符串
//	`str`所传输的数据格式如下：
//	close:关闭,open:开启 // 允许的状态列表，第一个为初始状态，使用:分割状态和状态描述
//	open:close->open     // 操作事件标识，如果写入了多个同名的事件运行时会运行第一个该名称时间
//	close:open->close    // 具体格式为： 事件名:允许状态->目标状态 （允许状态可以有多个）
func NewXstate(str string) *Xstate {
	inf := strings.Split(str, "\n")
	xs := Xstate{statusMap: map[string]string{}}
	for i := 0; i < len(inf); i++ {
		inf[i] = strings.TrimSpace(inf[i])
		if inf[i] == "" {
			xs.Error = errors.New("字符串起始行不能为空")
			return &xs
		}
		if i == 0 {
			// 确定初始状态
			spl := strings.Split(inf[i], ",")
			if len(spl) == 1 {
				xs.Error = errors.New("抱歉，单状态事件无法启用状态机")
				return &xs
			}
			for j := 0; j < len(spl); j++ {
				temp := strings.Split(spl[j], ":")
				if xs.start == "" {
					xs.start = temp[0]
				}
				if xs.current == "" {
					xs.current = xs.start
				}
				if len(temp) == 1 {
					xs.statusMap[temp[0]] = temp[0]
				} else {
					xs.statusMap[temp[0]] = strings.Join(temp[1:], ":")
				}
			}
		} else {
			// 切分事件列表
			ev := event{}
			spl := strings.Split(inf[i], ":")
			if len(spl) != 2 {
				continue
			}
			ev.Name = spl[0]
			// 切分起始点
			tem := strings.Split(spl[1], "->")
			if len(tem) != 2 {
				continue
			}
			ev.Origin = strings.Split(tem[0], ",")
			ev.Target = tem[1]
			xs.events = append(xs.events, ev)
		}
	}
	return &xs
}

// 操作事件类型定义
type event struct {
	Name   string   // 事件名称
	Origin []string // 允许的事件起止状态
	Target string   // 目标状态
}

// 判断e是否在s内
//
//	e	待判断的状态
//	s	允许的列表状态
func inArrayString(e string, s []string) bool {
	for i := 0; i < len(s); i++ {
		if e == s[i] {
			return true
		}
	}
	return false
}
